package com.sharp.core.raytrace;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import com.sharp.core.graphics.Color;
import com.sharp.core.graphics.Graphic;
import com.sharp.core.graphics.Light;
import com.sharp.core.graphics.Material;
import com.sharp.core.graphics.gl.OpenGLGraphic;
import com.sharp.core.math.primitives.Primitive;
import com.sharp.core.raytrace.shading.AmbientShading;
import com.sharp.core.raytrace.shading.Shading;

public class RayTracer extends Tracer<Primitive> {

	private Color c;
	
	private AmbientShading ambient = new AmbientShading();
	
	private BlockingQueue<Ray> rayQueue;
	
	public RayTracer(Camera cam, RayFactory caster, Shading shading,
			PrimitiveHolder objs) {
		super(cam, caster, shading, objs);
		
		rayQueue = new ArrayBlockingQueue<Ray>(cam.getWidth()*cam.getHeight());
		//new Thread(new Tracer(rayQueue), "Tracer-1").start();
		//new Thread(new Tracer(rayQueue, rayPool), "Tracer-2").start();
		//new Thread(new Tracer(rayQueue)).start();
	}

	@Override
	public void render() {
		for ( int x = 0; x < cam.getWidth(); x++ ) {
			for ( int y = 0; y < cam.getHeight(); y++ ) {
				Ray ray = new Ray(); 
				caster.cast(ray, x, y);
				Color c = trace(ray, 0);
				((OpenGLGraphic)Graphic.instance()).drawToBackBuffer(cam, ray.getScreenX(), ray.getScreenY(), c);
				//rayQueue.offer(ray);
			}
		}
		/*while( rayQueue.size() > 0 )
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}*/
	}
	
	private Color trace(Ray ray, int depth) {
		Intersection inter = objs.findIntersection(ray);
		Color c = new Color(Color.BLACK);
		if ( inter != null ) {
			if ( inter.getPrimitive().isLight() ) {
				c.setTo(inter.getPrimitive().getMaterial().getColor());
			} else {
				c.setTo(Color.BLACK);
				for ( Light light : objs.getLights() ) {
					if ( objs.isVisible(inter, light) == true )
						c.add(shading.calcColor(inter, light, c));
				}
				c.add(ambient.calcColor(inter, null, c));
				if ( depth < 3 ) {
					Ray reflectionRay = Ray.reflectRay(inter, ray, inter.getPrimitive());
					//Ray refractionRay;
					Material mat = inter.getPrimitive().getMaterial();
					Color sec = trace(reflectionRay, depth+1);
					c.add(sec.multiply(mat.getReflectivity()) );
					//sec = trace(refractionRay, depth+1);
					//c.add(sec.multiply(mat.getTransmittivity()) );
				}
			}
		}
		return c;
	}
	
	
	/*private class Tracer implements Runnable {

		private BlockingQueue<Ray> rayQueue;
		
		Tracer(BlockingQueue<Ray> rayQ) {
			rayQueue = rayQ;
		}
		
		@Override
		public void run() {
			while ( true ) {
					Ray ray = rayQueue.poll();
					Color c = trace(ray, 0);
					((OpenGLGraphic)Graphic.instance()).drawToBackBuffer(ray.getScreenX(), ray.getScreenY(), c);
			}
		}
	}*/
}
